﻿using System;

using Microsoft.Extensions.Logging;

using Adaptive.Agrona.Concurrent.RingBuffer;

using Unity.Commons.Client.Buffer;
using Unity.Commons.Client.Exceptions;
using Unity.Commons.Client.Generator;

using Unity.Environment;
using Unity.Environment.Exceptions;
using Unity.Logger;

namespace Unity.Commons.Client.Configuration
{
    public class PropertyConfiguration : DefaultConfiguration
    {
        private static readonly ILogger Logger = UnityLogger.ConsoleLoggerFactory.CreateLogger(nameof(PropertyConfiguration));
        
        private static readonly int MainBufferSize = ReadProperty(
            UnityParameter.MainBufferSize.Value(),
            DefaultMainBufferSize
        );

        private static readonly int OfflineBufferSize = ReadProperty(
            UnityParameter.OfflineBufferSize.Value(),
            DefaultOfflineBufferSize
        );

        private static int ReadProperty(string propertyName, int defaultValue)
        {
            if(string.IsNullOrWhiteSpace(propertyName))
            {
                throw new PropertyConfigurationException("Argument propertyName must be not null");
            }
            else
            {
                try
                {
                    int bufferSize = UnityEnvironment.GetIntegerParameter(propertyName);
                    if (bufferSize <= 0)
                    {
                        string errorMessage = $"Environment property '{propertyName}' must be greater than zero";
                        Logger.LogError(errorMessage);
                        throw new PropertyConfigurationException(errorMessage);
                    }
                    return CalculateBufferSize(bufferSize);
                } 
                catch(EnvironmentParameterNotFoundException exception)
                {
                    Logger.LogWarning(exception, $"Environment property '{propertyName}' not found. Used default value {defaultValue}");
                    return CalculateBufferSize(defaultValue);
                }
                catch (Exception exception)
                {
                    Logger.LogWarning(exception, $"Error parsing environment property '{propertyName}'. Used default value {defaultValue}");
                    return CalculateBufferSize(defaultValue);
                }
            }
        }

        private static int CalculateBufferSize(int size)
        {
            if(size < 1)
            {
                throw new PropertyConfigurationException("Buffer size can't be less than 1");
            }
            if(size > MaximumBufferSizeMessages)
            {
                throw new PropertyConfigurationException($"Buffer size can't be more than {MaximumBufferSizeMessages}");
            }

            int prepareCalcByteBufferSize = size * UnityRingBuffer.GetRecordLength();
            int byteBufferSize = HighestOneBit(prepareCalcByteBufferSize - 1) * 2;
            while(byteBufferSize >> 3 < UnityGenerator.MessageLength)
            {
                byteBufferSize *= 2;
            }
            byteBufferSize += RingBufferDescriptor.TrailerLength;
            return byteBufferSize;
        }

        public new int GetMainBufferSize()
        {
            return MainBufferSize;
        }

        public new int GetOfflineBufferSize()
        {
            return OfflineBufferSize;
        }

    }
}
